﻿// Copyright 2012 Jack Bulan (agentgeo)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;

using GemWorkshop.Formats.Nitro;
using GemWorkshop.Drawing3D;
using GemWorkshop.Formats.Nsbmd;
using GemWorkshop.Formats.Nsbtx;

namespace GemWorkshop.Map
{
    public partial class MapEditor : Form
    {
        private Narc mapsNarc, texturesNarc;
        private MapLoader mapLoader;
        private MatrixLoader matrixLoader;

        private bool mapLoaded = false, objChosen = false;
        private int currentMap = 0, currentHeader = 0;

        //private int SCALE = 4;

        private ModelViewer nsbmView;

        public MapEditor(MatrixLoader matrixLoader, int header = 0, int map = 0)
        {
            InitializeComponent();
            bSave.Enabled = false;

            this.matrixLoader = matrixLoader;

            mapsNarc = new Narc(Form1.GameDirectory + "\\" + Form1.GameSettings.ReadValue(Form1.Game, "MapData"));
            //for (int i = 0; i < nr.FileCount; i++)
            //{
            //    listMaps.Items.Add(string.Format("Map {0}", i));
            //}

            texturesNarc = new Narc(Form1.GameDirectory + "\\" + Form1.GameSettings.ReadValue(Form1.Game, "MapTextures"));
            nTextures1.Maximum = texturesNarc.FileCount;
            nTextures2.Maximum = 255; // Don't know...

            mapLoader = new MapLoader(mapsNarc, texturesNarc, Form1.GameDirectory + "\\arm9.bin");

            nsbmView = new ModelViewer();

            cMovValue.SelectedIndex = 0;

            tabControl1.Enabled = false;
            bObjAdd.Enabled = false;
            bObjRemove.Enabled = false;

            LoadMap(header, map);
        }

        private void LoadMap(int header, int map)
        {
            // Load a map!
            currentMap = map;
            currentHeader = header;
            mapLoader.Load(currentHeader, currentMap);
            mapLoaded = true;
            tabControl1.Enabled = true;

            pMovements.Invalidate();

            // Show objects
            listObjects.Items.Clear();
            if (mapLoader.HasObjects)
            {
                for (int i = 0; i < mapLoader.Objects.Length; i++)
                {
                    listObjects.Items.Add(string.Format("Object {0}", (i + 1)));
                }
            }
            objChosen = false;

            // Show map view
            tabPage3.Controls.Remove(nsbmView);
            nsbmView.Dispose();
            nsbmView = new ModelViewer(mapLoader.Nsbmd, mapLoader.Texture);
            nsbmView.Location = new Point(6, 6);
            nsbmView.Dock = DockStyle.Fill;
            nsbmView.Parent = splitContainer1.Panel1;
            splitContainer1.Panel1.Controls.Add(nsbmView);

            // Show header
            nTextures1.Value = mapLoader.Header.Textures;
            nTextures2.Value = mapLoader.Header.ObjectTexutres;
            nEvents.Value = mapLoader.Header.Events;
            nScripts.Value = mapLoader.Header.Scripts;
            nTexts.Value = mapLoader.Header.Texts;
            nWeather.Value = mapLoader.Header.Weather;
            nWilds.Value = mapLoader.Header.WildPokemon;
            nName.Value = mapLoader.Header.Name;
            nNameStyle.Value = mapLoader.Header.NameStyle;
            nMusicDay.Value = mapLoader.Header.MusicDay;
            nMusicNight.Value = mapLoader.Header.MusicNight;
            nCamera.Value = mapLoader.Header.Camera;
            nFlags.Value = mapLoader.Header.Flags;
            nMatrix.Value = mapLoader.Header.Matrix;

            // Load and show the matrix
            //if (ml.Header.Matrix > 0)
            //{
                matrixLoader.Load(mapLoader.Header.Matrix);
                // Matrix Properties
                tMatrixName.MaxLength = matrixLoader.header.Name.Length;
                tMatrixName.Text = matrixLoader.header.Name;
                nMatrixWidth.Value = matrixLoader.header.Width;
                nMatrixHeight.Value = matrixLoader.header.Height;
                DataTable matrix = new DataTable();
                for (int i = 0; i < matrixLoader.header.Width; i++)
                    matrix.Columns.Add(i.ToString());

                for (int y = 0; y < matrixLoader.header.Width; y++)
                {
                    //matrix.Columns.Add(x.ToString());
                    var row = matrix.NewRow();
                    for (int x = 0; x < matrixLoader.header.Height; x++)
                    {
                        row[x.ToString()] = matrixLoader.Layer1[x, y].ToString();
                    }
                    matrix.Rows.Add(row);
                }
                gridMatrix.Columns.Clear();
                gridMatrix.DataSource = matrix;
            //}

            // Update display
            this.Text = "Map Editor - " + mapLoader.Name;

            bSave.Enabled = true;
        }

        private void bSave_Click(object sender, EventArgs e)
        {
            // Set the header stuff :D
            mapLoader.Header.Textures = (byte)nTextures1.Value;
            mapLoader.Header.ObjectTexutres = (byte)nTextures2.Value;
            mapLoader.Header.Events = (ushort)nEvents.Value;
            mapLoader.Header.Scripts = (ushort)nScripts.Value;
            mapLoader.Header.Texts = (ushort)nTexts.Value;
            mapLoader.Header.MusicDay = (ushort)nMusicDay.Value;
            mapLoader.Header.MusicNight = (ushort)nMusicNight.Value;
            mapLoader.Header.Weather = (byte)nWeather.Value;
            mapLoader.Header.WildPokemon = (ushort)nWilds.Value;
            mapLoader.Header.Name = (ushort)nName.Value;
            mapLoader.Header.NameStyle = (byte)nNameStyle.Value;
            mapLoader.Header.Camera = (byte)nCamera.Value;
            mapLoader.Header.Flags = (byte)nFlags.Value;
            
            // Now save :D
            MapSaver ms = new MapSaver(mapLoader);
            ms.Save(currentHeader, currentMap);
        }

        private void bCross_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        #region Movements

        private void rMovPerm_CheckedChanged(object sender, EventArgs e)
        {
            cMovValue.Visible = rMovPerm.Checked;
            label1.Visible = rMovPerm.Checked;
            label2.Visible = !rMovPerm.Checked;

            pMovements.Invalidate();
        }

        private void pMovements_Paint(object sender, PaintEventArgs e)
        {
            // Draw our movements...
            if (mapLoaded)
            {
                for (int x = 0; x < 32; x++)
                {
                    for (int y = 0; y < 32; y++)
                    {
                        Rectangle dest = new Rectangle(x * 16, y * 16, 16, 16);

                        byte value;
                        if (rMovPerm.Checked)
                        {
                            value = mapLoader.Movements[x, y].Permission;
                            if (value == 0x00)
                                e.Graphics.FillRectangle(Brushes.White, dest);
                            else if (value == 0x02 || value == 0x03)
                                e.Graphics.FillRectangle(Brushes.Green, dest);
                            else if (value > 0x09 && value < 0x16)
                                e.Graphics.FillRectangle(Brushes.Blue, dest);
                            else if (value == 0x21)
                                e.Graphics.FillRectangle(Brushes.Tan, dest);
                            else if (value >= 0xED)
                                e.Graphics.FillRectangle(Brushes.White, dest);
                            else
                                e.Graphics.FillRectangle(Brushes.LightGray, dest);

                            
                        }
                        else
                        {
                            value = mapLoader.Movements[x, y].Flag;

                            if (value == 0x80)
                                e.Graphics.FillRectangle(Brushes.Red, dest);
                            else
                                e.Graphics.FillRectangle(Brushes.Green, dest);
                        }

                        e.Graphics.DrawString(value.ToString("X2"),
                            new Font("Arial", 8.5f),
                                Brushes.Black, (x * 16) + 0, (y * 16) + 2);
                    }
                }
            }
        }

        private void pMovements_MouseDown(object sender, MouseEventArgs e)
        {
            Movements_Click(e);
        }

        private void pMovements_MouseMove(object sender, MouseEventArgs e)
        {
            Movements_Click(e);
        }

        private void Movements_Click(MouseEventArgs e)
        {
            int x = e.X / 16;
            int y = e.Y / 16;
            lblXY.Text = string.Format("X: {0}, Y: {1}", x, y);

            if (e.Button == MouseButtons.Left)
            {
                if (rMovFlags.Checked)
                {
                    mapLoader.Movements[x, y].Flag = 0;
                    pMovements.Invalidate();
                }
                else
                {
                    mapLoader.Movements[x, y].Permission = (byte)cMovValue.SelectedIndex;
                    pMovements.Invalidate();
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                if (rMovFlags.Checked)
                {
                    mapLoader.Movements[x, y].Flag = 0x80;
                    pMovements.Invalidate();
                }
            }
        }

        #endregion

        #region Objects

        private void listObjects_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Get object info.
            int index = listObjects.SelectedIndex;
            nObjNo.Value = mapLoader.Objects[index].Number;

            nObjX.Value = mapLoader.Objects[index].X;
            nObjY.Value = mapLoader.Objects[index].Y;
            nObjZ.Value = mapLoader.Objects[index].Z;

            tObjXFlag.Text = mapLoader.Objects[index].XFlag.ToString();
            tObjYFlag.Text = mapLoader.Objects[index].YFlag.ToString();
            tObjZFlag.Text = mapLoader.Objects[index].ZFlag.ToString();

            tObjWidth.Text = mapLoader.Objects[index].Width.ToString();
            tObjHeight.Text = mapLoader.Objects[index].Height.ToString();
            tObjLength.Text = mapLoader.Objects[index].Length.ToString();

            objChosen = true;

            // Set up cube

            pObjPos.Invalidate();
        }

        private void tObjWidth_TextChanged(object sender, EventArgs e)
        {
            
        }

        private void tObjHeight_TextChanged(object sender, EventArgs e)
        {

        }

        private void tObjLength_TextChanged(object sender, EventArgs e)
        {
            
        }

        private void objPre_RotateChanged(object sender, EventArgs e)
        {
            
        }

        private void bObjSave_Click(object sender, EventArgs e)
        {
            int index = listObjects.SelectedIndex;

            mapLoader.Objects[index].Number = (int)nObjNo.Value;

            mapLoader.Objects[index].X = (short)nObjX.Value;
            mapLoader.Objects[index].Y = (short)nObjY.Value;
            mapLoader.Objects[index].Z = (short)nObjZ.Value;

            mapLoader.Objects[index].XFlag = ushort.Parse(tObjXFlag.Text);
            mapLoader.Objects[index].YFlag = ushort.Parse(tObjYFlag.Text);
            mapLoader.Objects[index].ZFlag = ushort.Parse(tObjZFlag.Text);

            mapLoader.Objects[index].Width = int.Parse(tObjWidth.Text);
            mapLoader.Objects[index].Height = int.Parse(tObjHeight.Text);
            mapLoader.Objects[index].Length = int.Parse(tObjLength.Text);
        }

        private void pObjPos_Paint(object sender, PaintEventArgs e)
        {
            try
            {
                if (mapLoaded)
                {
                    for (int x = 0; x < 32; x++)
                    {
                        for (int y = 0; y < 32; y++)
                        {
                            Rectangle dest = new Rectangle(x * 8, y * 8, 8, 8);
                            byte value = mapLoader.Movements[x, y].Flag;

                            if (value == 0x80)
                                e.Graphics.FillRectangle(Brushes.Red, dest);
                            else
                                e.Graphics.FillRectangle(Brushes.Green, dest);
                        }
                    }

                    if (objChosen)
                    {
                        e.Graphics.FillRectangle(Brushes.Purple,
                            (float)(nObjX.Value - 1) * 8,
                            (float)(nObjZ.Value - 1) * 8, 8, 8);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        #endregion


        private void bNsbmdExp_Click(object sender, EventArgs e)
        {
            saveFileDialog1.Title = "Export Map...";
            saveFileDialog1.Filter = "Nintendo Models|*.nsbmd";
            saveFileDialog1.FileName = "";

            if (saveFileDialog1.ShowDialog() != DialogResult.OK) return;

            bool result = mapLoader.ExportNsbmd(saveFileDialog1.FileName, currentMap, false);

            if (result) MessageBox.Show("Export Success!");
            else MessageBox.Show("Export Failed!");
        }

        private void bNsbmdImp_Click(object sender, EventArgs e)
        {

        }

        private void bNsbmdExpTex_Click(object sender, EventArgs e)
        {
            saveFileDialog1.Title = "Export Map...";
            saveFileDialog1.Filter = "Wavefront Object Files|*.obj";
            saveFileDialog1.FileName = "";

            if (saveFileDialog1.ShowDialog() != DialogResult.OK) return;

            //bool result = ml.ExportNsbmd(saveFileDialog1.FileName, currentMap, true);

            NsbmdToObj objConv = new NsbmdToObj();
            objConv.Convert(saveFileDialog1.FileName, nsbmView.Model, nsbmView.Texture);
        }

        private void bNsbmdImpTex_Click(object sender, EventArgs e)
        {

        }

        private void bChangeTextures_Click(object sender, EventArgs e)
        {
            // TODO: Update preview
        }
    }
}
